/******************************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Ltd.                       */
/*                                                                            */
/*  NAME      = sybase_request.c                                              */
/*  NOTE      = TDS协议解析                                                   */
/*  DATE      = 2014/05/27 by zhengxd                                         */
/******************************************************************************/
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>

#include "sybase_core.h"
#include "sybase_common.h"
#include "sybase_token.h"

extern void *g_p_apm_context;

/*******************************************************************************
*  FUNC     :  上报响应结果信息
*  ARGS     :  p_st_conn(入力链接信息)
            :  p_st_private(MSSQL私有信息)
            :  p_st_hash_report((Frhash_header *))
            :  i_sql_rpc(MSSQL_RPC/MSSQL_SQL)
*  RTN      :  APM_L7_ANALYZE_SUCC  (success)
*              APM_L7_ANALYZE_FAIL  (failed)
*  NOTE     :
*******************************************************************************/
static int __sybase_report_data(Conntrack *p_st_conn
                               , L7session_sybase_private *p_st_private
                               , Frhash_header *p_st_hash_report
                               , int i_fetch_flg)
{
    int i_ret = FR_SUCC;
    Frreport_body_data *p_st_report_body = NULL;
    L7_req_sybase *p_st_req = p_st_private->p_st_req;
    L7_resp_sybase *p_st_resp = p_st_private->p_st_resp;
    L7_fragment_sybase *p_st_fragment = p_st_private->p_st_fragment;
    Sql_resultset *p_st_resultset = p_st_req->p_st_resultset;
    Sql_statement *p_st_statement = p_st_req->p_st_statement;

    /* 更新response结果 */
    if (i_fetch_flg == FR_YES)
    {
        i_ret = frdb_set_resultset_resp2(p_st_resultset
                                        , p_st_fragment->ui_total_len
                                        , 1
                                        , &p_st_resp->ui_fetch_rows
                                        , &(p_st_fragment->st_first_timestamp)
                                        , &(p_st_fragment->st_end_timestamp));
    }
    else
    {
        i_ret = frdb_set_resultset_resp2(p_st_resultset
                                        , p_st_fragment->ui_total_len
                                        , 1
                                        , &p_st_resp->ui_effect_rows
                                        , &(p_st_fragment->st_first_timestamp)
                                        , &(p_st_fragment->st_end_timestamp));
    }
    if (i_ret != FR_SUCC)
    {
        frdbg("frdb_set_resultset_resp2 failed!\n");
        /* 销毁request和response数据 */
        //sybase_destroy_request(&p_st_private->p_st_req);
        //sybase_destroy_response(&p_st_private->p_st_resp);
        return APM_L7_ANALYZE_FAIL;
    }

    /* 创建report body */
    p_st_report_body = sybase_new_report_body(p_st_conn, p_st_private->p_st_sess);
    /* 创建report body失败时，不释放做成的request */
    if (p_st_report_body == NULL)
    {
        frdbg("sqlserver_new_report_body failed!\n");
        /* 销毁request和response数据 */
        //sybase_destroy_request(&p_st_private->p_st_req);
        //sybase_destroy_response(&p_st_private->p_st_resp);
        return APM_L7_ANALYZE_FAIL;
    }

    /* 上报request数据 */
    i_ret = frdb_report_resp2(p_st_conn, p_st_hash_report, p_st_statement
                             , p_st_resultset, p_st_resp->i_error_num
                             , p_st_resp->p_error_msg
                             , p_st_resp->p_error_msg, &p_st_report_body);
    /* 销毁request和response数据 */
    //sybase_destroy_request(&p_st_private->p_st_req);
    //sybase_destroy_response(&p_st_private->p_st_resp);
    /* 上报request数据失败时，不释放做成的request */
    if (i_ret != FR_SUCC)
    {
        frdbg("frdb_report_resp2 failed!\n");
        frreport_body_free(&p_st_report_body);
        return APM_L7_ANALYZE_FAIL;
    }

    return APM_L7_ANALYZE_SET_RESP(APM_L7_ANALYZE_SUCC);
}

/*******************************************************************************
*  FUNC     :  解析TOKENIZED TDS REQUEST
*  ARGS     :  p_st_conn(入力链接信息)
*           :  p_st_private(私有session数据)
*           :  p_st_hash_report(保存Frreport_data)
*           :  i_whole_flg(packert完整flag)
*  RTN      :  APM_L7_ANALYZE_FAIL      (FAIL)
*              APM_L7_ANALYZE_TODO      (More data)
*              APM_L7_ANALYZE_SUCC      (SUCC)
*  NOTE     :
*******************************************************************************/
int sybase_parse_response(Conntrack *p_st_conn
                         , L7session_sybase_private *p_st_private
                         , int i_packet_type
                         , Frhash_header *p_st_hash_report
                         , int i_whole_flg)
{
    uint8_t u8_token_type = 0;
    int i_ret = APM_L7_ANALYZE_SUCC;
    int i_angin_flg = FR_NO;
    int i_options = 0;
    int i_dynamic_type = 0;
    L7_resp_sybase *p_st_resp = NULL;
    Sql_resultset *p_st_resultset = NULL;
    L7_req_sybase *p_st_req = p_st_private->p_st_req;
    L7session_sybase *p_st_session = p_st_private->p_st_sess;
    L7_fragment_sybase *p_st_fragment = p_st_private->p_st_fragment;
    Frbyte_Packet *p_st_data = p_st_fragment->p_st_data;
    Frreport_data *p_st_report_data=NULL;
    char arr_info[128] = {0};

    /* 判断是否收到request,没有收到request就不用解析response */
    if (p_st_req == NULL)
    {
        /* 需要接受完所有数据 */
        if (i_whole_flg == FR_NO)
        {
            frdbg("No request information but this response is uncompleted!\n");
            return APM_L7_ANALYZE_TODO;
        }
        else
        {
            frdbg("No request information!\n");
            return APM_L7_ANALYZE_SUCC;
        }
    }

    /* 创建response */
    if (p_st_private->p_st_resp == NULL)
    {
        p_st_private->p_st_resp = (L7_resp_sybase *)frmalloc(sizeof(L7_resp_sybase));
        if (p_st_private->p_st_resp == NULL)
        {
            return APM_L7_ANALYZE_FAIL;
        }
        /* 初始化response */
        memset(p_st_private->p_st_resp, 0x0, sizeof(L7_resp_sybase));
        p_st_private->p_st_req->u8_packet_type = i_packet_type;
    }
    p_st_resp = p_st_private->p_st_resp;

    bytebuff_setPosition(p_st_data, p_st_fragment->ui_parsed_index);
    while (bytebuff_remaining(p_st_data) > 0)
    {
        bytebuff_get_char(p_st_data, &u8_token_type);
        switch(u8_token_type)
        {
            case TDS50_TOKEN_ALTFMT:
                i_ret = sybase_parse_altfmt_token(p_st_data, i_whole_flg
                                                 , p_st_session
                                                 , &p_st_resp->st_altfmt);
                break;

            case TDS50_TOKEN_ALTNAME:
                i_ret = sybase_parse_altname_token(p_st_data, i_whole_flg
                                                  , p_st_session);
                break;

            case TDS50_TOKEN_ALTROW:
                i_ret = sybase_parse_altrow_token(p_st_data, i_whole_flg
                                                 , p_st_session
                                                 , &p_st_resp->st_altfmt);
                break;

            case TDS50_TOKEN_CAPABILITY:
                 i_ret = sybase_parse_capability_token(p_st_data, i_whole_flg
                                                      , FR_SRV_IP_PORT
                                                      , p_st_session);
                break;

            case TDS50_TOKEN_COLINFO:
                i_ret = sybase_parse_colinfo_token(p_st_data, i_whole_flg
                                                  , p_st_session);
                break;

            case TDS50_TOKEN_CONTROL:
                i_ret = sybase_parse_control_token(p_st_data, i_whole_flg
                                                  , p_st_session);
                break;

            case TDS50_TOKEN_CURINFO:
                i_ret = sybase_parse_curinfo_token(p_st_data, i_whole_flg
                                                  , p_st_session
                                                  , &p_st_resp->l_cursor_id
                                                  , p_st_resp->arr_cursor_name);
                break;

            case TDS50_TOKEN_CURINFO2:
                i_ret = sybase_parse_curinfo2_token(p_st_data, i_whole_flg
                                                   , p_st_session
                                                   , &p_st_resp->l_cursor_id
                                                   , p_st_resp->arr_cursor_name);
                break;

            case TDS50_TOKEN_CURINFO3:
                i_ret = sybase_parse_curinfo3_token(p_st_data, i_whole_flg
                                                   , p_st_session
                                                   , &p_st_resp->l_cursor_id
                                                   , p_st_resp->arr_cursor_name);
                break;

            case TDS50_TOKEN_DONE:
                i_ret = sybase_parse_done_token(p_st_data, i_whole_flg
                                               , p_st_session
                                               , &p_st_resp->i_err_flg
                                               , &p_st_resp->ui_effect_rows);
                break;

            case TDS50_TOKEN_DONEINPROC:
            case TDS50_TOKEN_DONEPROC:
                i_ret = sybase_parse_doneproc_token(p_st_data, i_whole_flg
                                                   , p_st_session
                                                   , &p_st_resp->i_err_flg
                                                   , &p_st_resp->ui_effect_rows);
                break;

            case TDS50_TOKEN_DYNAMIC:
                i_ret = sybase_parse_dynamic_token(p_st_data, i_whole_flg
                                                  , p_st_session
                                                  , FR_SRV_IP_PORT
                                                  , &i_dynamic_type
                                                  , &i_options
                                                  , p_st_resp->arr_id
                                                  , NULL);
                break;

            case TDS50_TOKEN_DYNAMIC2:
                i_ret = sybase_parse_dynamic2_token(p_st_data, i_whole_flg
                                                   , p_st_session
                                                   , FR_SRV_IP_PORT
                                                   , &i_dynamic_type
                                                   , &i_options
                                                   , p_st_resp->arr_id
                                                   , NULL);
                break;

            case TDS50_TOKEN_EED:
                i_ret = sybase_parse_eed_token(p_st_data, i_whole_flg
                                              , p_st_session
                                              , &p_st_resp->i_error_num
                                              , &p_st_resp->p_error_msg);
                break;

            case TDS50_TOKEN_ENVCHANGE:
                i_ret = sybase_parse_envchange_token(p_st_data, i_whole_flg
                                                    , p_st_session);
                break;

            case TDS50_TOKEN_ERROR:
                i_ret = sybase_parse_error_token(p_st_data, i_whole_flg
                                                , p_st_session
                                                , &p_st_resp->i_error_num
                                                , &p_st_resp->p_error_msg);
                break;

            case TDS50_TOKEN_EVENTNOTICE:  // 0xA2
                i_ret = sybase_parse_eventnotice_token(p_st_data, i_whole_flg
                                                      , p_st_session);
                break;

            case TDS50_TOKEN_INFO:
                i_ret = sybase_parse_info_token(p_st_data, i_whole_flg
                                               , p_st_session
                                               , &p_st_resp->i_error_num
                                               , &p_st_resp->p_error_msg);
                break;

            case TDS50_TOKEN_KEY:
                i_ret = sybase_parse_key_token(p_st_data, i_whole_flg
                                              , p_st_session
                                              , &p_st_resp->st_metadata_param);
                break;

            case TDS50_TOKEN_LOGINACK:
                i_ret = sybase_parse_loginack_token(p_st_data, i_whole_flg
                                                   , p_st_session
                                                   , &p_st_req->i_login_status);
                break;

            case TDS50_TOKEN_MSG:
                i_ret = sybase_parse_msg_token(p_st_data, i_whole_flg
                                              , p_st_session);
                break;

            case TDS50_TOKEN_OFFSET:
                i_ret = sybase_parse_offset_token(p_st_data, i_whole_flg
                                                 , p_st_session);
                break;

            case TDS50_TOKEN_OPTIONCMD:
                i_ret = sybase_parse_optioncmd_token(p_st_data, i_whole_flg
                                                     , p_st_session);
                break;

            case TDS50_TOKEN_ORDERBY:
                i_ret = sybase_parse_orderby_token(p_st_data, i_whole_flg
                                                   , p_st_session);
                break;

            case TDS50_TOKEN_ORDERBY2:
                i_ret = sybase_parse_orderby2_token(p_st_data, i_whole_flg
                                                    , p_st_session);
                break;

            case TDS50_TOKEN_PARAMFMT:
                frlist_remove_all(&p_st_resp->st_metadata_param, frdb_metadata_node_free);
                i_ret = sybase_parse_paramfmt_token(p_st_data, i_whole_flg
                                                   , p_st_session
                                                   , &p_st_resp->st_metadata_param);
                break;

            case TDS50_TOKEN_PARAMFMT2:
                i_ret = sybase_parse_paramfmt2_token(p_st_data, i_whole_flg
                                                    , p_st_session
                                                    , &p_st_resp->st_metadata_param);
                break;

            case TDS50_TOKEN_PARAMS:
                i_ret = sybase_parse_params_token(p_st_data, i_whole_flg
                                                 , p_st_session
                                                 , &p_st_resp->st_metadata_param
                                                 , 0
                                                 , NULL);
                break;

            case TDS50_TOKEN_RETURNSTATUS:
                i_ret = sybase_parse_returnstatus_token(p_st_data, i_whole_flg
                                                       , p_st_session);
                break;

            case TDS50_TOKEN_RETURNVALUE:
                i_ret = sybase_parse_returnvalue_token(p_st_data, i_whole_flg
                                                      , p_st_session);
                break;

            case TDS50_TOKEN_ROW:
                if (p_st_req->p_st_statement
                    && frlist_size(&p_st_req->p_st_statement->st_metadata_column))
                {
                    i_ret = sybase_parse_row_token(p_st_data, i_whole_flg
                                                  , p_st_session
                                                  , &p_st_req->p_st_statement->st_metadata_column);
                }
                else
                {
                    i_ret = sybase_parse_row_token(p_st_data, i_whole_flg
                                                  , p_st_session
                                                  , &p_st_resp->st_metadata_column);
                }
                if (i_ret == APM_L7_ANALYZE_SUCC)
                {
                    p_st_resp->ui_fetch_rows += 1;
                }
                break;

            case TDS50_TOKEN_ROWFMT:
                i_ret = sybase_parse_rowfmt_token(p_st_data, i_whole_flg
                                                  , p_st_session
                                                  , &p_st_resp->st_metadata_column);
                break;

            case TDS50_TOKEN_ROWFMT2:
                i_ret = sybase_parse_rowfmt2_token(p_st_data, i_whole_flg
                                                  , p_st_session
                                                  , &p_st_resp->st_metadata_column);
                break;

            case TDS50_TOKEN_TABNAME:
                i_ret = sybase_parse_tabname_token(p_st_data, i_whole_flg
                                                  , p_st_session);
                break;

            default:
                i_ret = APM_L7_ANALYZE_FAIL;
                break;
        }

        if (i_ret == APM_L7_ANALYZE_SUCC)
        {
            p_st_fragment->ui_parsed_index = bytebuff_getPosition(p_st_data);
        }
        else if (i_ret == APM_L7_ANALYZE_FAIL)
        {
            if (p_st_session->i_confirmed_endian == FR_NO)
            {
                /* do again */
                if (i_angin_flg == FR_NO)
                {
                    /* 设置成大头 */
                    i_angin_flg = FR_YES;
                    p_st_session->i_lint2_endian = BYTE_BIG_ENDIAN;
                    p_st_session->i_lint4_endian = BYTE_BIG_ENDIAN;
                    p_st_session->i_lflt_type = FRTDS_FLT_IEEE_HI;
                    p_st_session->i_ldate_endian = BYTE_BIG_ENDIAN;
                    p_st_session->i_lnoshort_type = FR_NO;
                    p_st_session->i_lflt4_type = FRTDS_FLT_IEEE_HI;
                    p_st_session->i_ldate4_endian = BYTE_BIG_ENDIAN;
                    /* init response */
                    sybase_int_response(p_st_resp);
                    bytebuff_setPosition(p_st_data, 0);
                    p_st_fragment->ui_parsed_index = 0;
                }
                else
                {
                    /* 设置回原来的值 */
                    p_st_session->i_lint2_endian = BYTE_LITTLE_ENDIAN;
                    p_st_session->i_lint4_endian = BYTE_LITTLE_ENDIAN;
                    p_st_session->i_lflt_type = FRTDS_FLT_IEEE_LO;
                    p_st_session->i_ldate_endian = BYTE_LITTLE_ENDIAN;
                    p_st_session->i_lnoshort_type = FR_NO;
                    p_st_session->i_lflt4_type = FRTDS_FLT_IEEE_LO;
                    p_st_session->i_ldate4_endian = BYTE_LITTLE_ENDIAN;
                    return APM_L7_ANALYZE_FAIL;
                }
            }
        }
        else
        {
            return APM_L7_ANALYZE_TODO;
        }
    }

    /* 完整的request packet解析完了 */
    if (i_whole_flg == FR_YES)
    {
        if (p_st_req->i_req_type == TDS50_MSGTYPE_BUF_LOGIN)
        {
            switch (p_st_req->i_login_status)
            {
                case FRTDS_LOGIN_FAIL:
                    if (p_st_private->p_st_sess->p_db_user)
                    {
                        snprintf(arr_info, sizeof(arr_info), "%s login"
                                , p_st_private->p_st_sess->p_db_user);
                    }
                    else
                    {
                        snprintf(arr_info, sizeof(arr_info), "login");
                    }
                    p_st_report_data = frreport_update_create(p_st_conn,
                                                              APM_REPORT_UPDATE_LOGON_REJECT
                                                              , arr_info, &p_st_fragment->st_end_timestamp);
                    if (p_st_report_data)
                    {
                        i_ret = frreport_send(g_p_apm_context, &p_st_report_data);
                        if (i_ret != FR_SUCC)
                        {
                            return APM_L7_ANALYZE_FAIL;
                        }
                    }
                    sybase_destroy_curr_info(p_st_private);
                    i_ret = APM_L7_ANALYZE_SET_LOGIN(APM_L7_ANALYZE_SUCC);
                    return APM_L7_ANALYZE_SET_REQ(i_ret);

                case FRTDS_LOGIN_SUCCEED:
                    if (p_st_private->p_st_sess->p_db_user)
                    {
                        snprintf(arr_info, sizeof(arr_info), "%s login", p_st_private->p_st_sess->p_db_user);
                    }
                    else
                    {
                        snprintf(arr_info, sizeof(arr_info), "login");
                    }
                    p_st_report_data = frreport_update_create(p_st_conn,
                                                             APM_REPORT_UPDATE_LOGON
                                                             , arr_info, &p_st_fragment->st_end_timestamp);
                    if (p_st_report_data)
                    {
                        i_ret = frreport_send(g_p_apm_context, &p_st_report_data);
                        if (i_ret != FR_SUCC)
                        {
                            return APM_L7_ANALYZE_FAIL;
                        }
                    }
                    sybase_destroy_curr_info(p_st_private);
                    p_st_private->p_st_sess->st_login = p_st_fragment->st_end_timestamp;
                    i_ret = APM_L7_ANALYZE_SET_LOGIN(APM_L7_ANALYZE_SUCC);
                    return APM_L7_ANALYZE_SET_REQ(i_ret);

                default:
                    return APM_L7_ANALYZE_SUCC;
            }
        }

        switch (p_st_req->i_req_type)
        {
            /* 追加到hash */
            case TDS50_TOKEN_CURDECLARE:
            case TDS50_TOKEN_CURDECLARE2:
            case TDS50_TOKEN_CURDECLARE3:
            case TDS50_TOKEN_CUROPEN:
                /* add statement to hash with arr_id */
                i_ret = sybase_add_statement(p_st_private
                                            , SYBASE_CURSOR
                                            , FRTDS_DIRECT_RESPONSE);
                if (i_ret != APM_L7_ANALYZE_SUCC)
                {
                    frdbg("sybase_add_statement failed!\n");
                    sybase_destroy_curr_info(p_st_private);
                    return APM_L7_ANALYZE_FAIL;
                }
                break;

            case TDS50_TOKEN_CURFETCH:
                if (p_st_resp->i_err_flg == FR_NO)
                {
                    /* 实际上只是做response长度已经影响行数的更新 */
                    i_ret = frdb_set_resultset_resp2(p_st_req->p_st_resultset
                                                    , p_st_fragment->ui_total_len
                                                    , 1
                                                    , &p_st_resp->ui_fetch_rows
                                                    , &(p_st_fragment->st_first_timestamp)
                                                    , &(p_st_fragment->st_end_timestamp));
                    sybase_destroy_curr_info(p_st_private);
                    if (i_ret != FR_SUCC)
                    {
                        frdbg("frdb_set_resultset_resp2 failed!\n");
                        return APM_L7_ANALYZE_FAIL;
                    }
                    return APM_L7_ANALYZE_SET_RESP(APM_L7_ANALYZE_SUCC);
                }
                break;

            /* 删除hash */
            case TDS50_TOKEN_CURCLOSE:
                /* 需要删除cursor信息 */
                if (p_st_req->i_declare_flg)
                {
                    if (p_st_resp->l_cursor_id)
                    {
                        /* 只删除节点，不删除节点内容 */
                        frhash2_delLong(p_st_private->p_hash_prepare
                                       , p_st_req->l_cursor_id, NULL);
                        p_st_req->i_new_statement = FR_YES;
                    }
                    else
                    {
                        /* 只删除节点，不删除节点内容 */
                        i_ret = frhash2_delStr(p_st_private->p_hash_prepare
                                              , p_st_req->arr_cursor_name
                                              , strlen(p_st_resp->arr_cursor_name)
                                              , NULL);
                        if (i_ret != FR_SUCC)
                        {
                            frdbg("frhash2_delStr failed!\n");
                            return APM_L7_ANALYZE_FAIL;
                        }
                        p_st_req->i_new_statement = FR_YES;
                    }
                }
                break;

            default:
                break;

        }

        /* 更新数据 */
        if (p_st_resp->i_err_flg == FR_YES || p_st_req->i_report_flg == FR_YES)
        {
            i_ret = __sybase_report_data(p_st_conn, p_st_private
                                        , p_st_hash_report, FR_NO);
            if (p_st_req->p_st_statement)
            {
                p_st_resultset = frdb_get_last_resultset(p_st_req->p_st_statement);
                frdb_init_resultset(p_st_resultset, frct_seq_increase(p_st_conn));
                p_st_resultset->l_id = -1;
            }
        }
        else if (p_st_req->i_req_type != TDS50_TOKEN_LOGOUT)
        {
            /* 更新resultset */
            i_ret = frdb_set_resultset_resp2(p_st_req->p_st_resultset
                                          , p_st_fragment->ui_total_len
                                          , 0
                                          , NULL
                                          , &(p_st_fragment->st_first_timestamp)
                                          , &(p_st_fragment->st_end_timestamp));
            if (i_ret != FR_SUCC)
            {
                i_ret = APM_L7_ANALYZE_FAIL;
            }
        }
        sybase_destroy_curr_info(p_st_private);
        return i_ret;
    }
    else
    {
        return APM_L7_ANALYZE_TODO;
    }
}
